package com.tm.dao;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.transform.AliasToBeanResultTransformer;

import com.tm.util.HibernateSessionFactory;
import com.tm.util.SessionManager;
import com.util.BasePojo;
import com.util.LoggerHelper;


/**
 * Data access object (DAO) for domain model
 * 
 * @author MyEclipse Persistence Tools
 */
public class BaseHibernateDAO{
	private Logger logger = Logger.getLogger(BaseHibernateDAO.class);
	
	public Session getSession() {
		return HibernateSessionFactory.getSession();
	}
	
	protected Session session = null;
	
	protected void closeSession(){
		if(session != null){
			session.close();
		}
	}
	
	protected Integer getSessionUserId(){
		SessionManager s = SessionManager.getCurrentSessionManager();
		if(s != null){
			return s.getUserId() == null ? 0 : s.getUserId();
		}else{
			return 0;
		}
	}
	
	protected void save(BasePojo pojo) {
		try{
			session = this.getSession();
			session.save(pojo);
			session.flush();
		}catch(Exception e){
			logger.error(LoggerHelper.whoInvokedMe(3)+e.getMessage());
			throw new RuntimeException(e.getMessage());
		}finally{
			session.close();
		}
	}
	
	protected void delete(BasePojo pojo) {
		try{
			//session = this.getSession();
			if(pojo == null || pojo.getId() == null){
				throw new RuntimeException("Object is null can't delete");
			}
			pojo = this.findById(pojo.getClass(), pojo.getId());
			if(pojo != null && pojo.getId() != null){
				//setFiledsToDeleted(pojo, userId);
				pojo.setDeleted(true);
				if(session != null){
					session.update(pojo);
					session.flush();
				}else{
					throw new RuntimeException("session is null when delete");
				}
			}
		}catch(Exception e){
			logger.error(LoggerHelper.whoInvokedMe(3)+e.getMessage());
			throw new RuntimeException(e.getMessage());
		}finally{
			session.close();
		}
	}
	
		
	protected void update(BasePojo pojo) {
		try{
			session = this.getSession();
			session.clear();
			session.update(pojo);
			session.flush();
		}catch(Exception e){
			logger.error(LoggerHelper.whoInvokedMe(3)+ e.getMessage()+e.getCause());
			throw new RuntimeException(e.getMessage());
		}finally{
			session.close();
		}
	}
	
	
	protected void saveOrUpdate(BasePojo pojo) {
		try{
			session = this.getSession();
			if (pojo.getId() == null) {
				session.save(pojo);
			} else {
				session.merge(pojo);
			}
			session.flush();
		}catch(Exception e){
			logger.error(LoggerHelper.whoInvokedMe(3)+e.getMessage());
			throw new RuntimeException(e.getMessage());
		}finally{
			session.close();
		}
	}
	
	
	protected BasePojo findById(Class pojoClass, Integer id, boolean withDeleted) {
		try{
			if(id == null){
				throw new RuntimeException("id is null for find");
			}else if(pojoClass == null){
				throw new RuntimeException("pojoClass is required when find by id");
			}
			Class superClass = pojoClass.getSuperclass();
			if(superClass != null && superClass.getSimpleName().equals(BasePojo.class.getSimpleName())){
				String hql = "from "+pojoClass.getSimpleName()+" where id = :id ";
				if(!withDeleted){
					hql +=" and deleted = 0";
				}
				Map param = new HashMap();
				param.put("id", id);
				return (BasePojo)this.executeUniqueHQLQuery(param, hql);
			}else{
				throw new RuntimeException("Could not search for "+pojoClass.getSimpleName()+" using hql query");
			}
		}catch(Exception e){
			logger.error(LoggerHelper.whoInvokedMe(3)+e.getMessage());
			this.closeSession();
			throw new RuntimeException(e.getMessage());
		}
	}
	
	public List<BasePojo> findByProperties(Map<String, Object> map,Class pojoClass){
		StringBuffer bf = new StringBuffer();
		bf.append("from ");
		bf.append(pojoClass.getSimpleName());
		bf.append(" where 1 = 1");
		Set<String> set = map.keySet();
		Iterator<String> it = set.iterator();
		String key =  null;
		while(it.hasNext()){
			key = it.next();
			bf.append(" and ");
			bf.append(key);
			bf.append(" = :");
			bf.append(key);
			bf.append(" ");
		}
		return this.executeHQLQuerys(map, bf.toString());
	}
	
	
	public BasePojo findById(Class pojoClass, Integer id) {
		return this.findById(pojoClass, id, false);
	}
	
	protected BasePojo findByIdWithDeleted(Class pojoClass, Integer id) {
		return this.findById(pojoClass, id, true);
	}

	
	private Object executeHQL(Map params,String hql,boolean isQuery, boolean resultMoreThanOne, Integer fetchStart, Integer fetchSize){
		Object result = null;
		try {
			session = getSession();
			Query query = session.createQuery(hql);
			if(params != null){
				query.setProperties(params);
			}
			if(isQuery){
				session.clear();
				if(resultMoreThanOne){
					if(fetchSize != null){
						if(fetchStart == null){
							fetchStart = 0;
						}
						query.setFirstResult(fetchStart);
						query.setFetchSize(fetchSize);
					}
					result = query.list();
				}else{
					result = query.uniqueResult();
				}
			}else{
				query.executeUpdate();
				session.flush();
				session.close();
			}
		} catch (Exception e) {
			this.closeSession();
			logger.error(LoggerHelper.whoInvokedMe(3)+" Error when executing Hibernate sql query "+e.getMessage()+"\n"+hql);
			System.err.println(e.getCause()+"\n"+hql);
			throw new RuntimeException(e.getMessage());
		}
		return result;
	}
	
	
	private Object executeSQL(Map params,String sql,Class TransFormTo, boolean isQuery, boolean resultMoreThanOne){
		Object result = null;
		try {
			session = getSession();
			Query query = session.createSQLQuery(sql);
			if(TransFormTo != null){
				query.setResultTransformer(new AliasToBeanResultTransformer(TransFormTo));
			}
			if(params != null && params.size() > 0){
				query.setProperties(params);
			}
			if(isQuery){
				if(resultMoreThanOne){
					result = query.list();
				}else{
					result = query.uniqueResult();
				}
			}else{
				result = query.executeUpdate();
			}
		} catch (Exception e) {
			logger.error(LoggerHelper.whoInvokedMe(3)+" Error when executing Hibernate sql query "+e.getMessage()+"\n"+sql);
			System.err.println(e.getCause()+"\n"+sql);
			throw new RuntimeException(e.getMessage());			
		}finally{
			session.close();
		}
		return result;
	}
	
	protected Integer exeucteSQLUpdate(Map params,String sql){
		return (Integer)this.executeSQL(params, sql, null, false, false);
	}
	
	protected Object executeUniqueSQLQuery(Map params,String sql,Class TransFormTo){
		return this.executeSQL(params, sql, TransFormTo, true, false);
	}
	
	protected List executeSQLQuerys(Map params,String sql,Class TransFormTo){
		return (List)this.executeSQL(params, sql, TransFormTo, true, true);
	}
	
	protected void exeucteHQLUpdate(Map params,String hql){
		this.executeHQL(params, hql, false, false, null, null);
	}
	
	protected BasePojo executeUniqueHQLQuery(Map params,String hql){
		return (BasePojo)this.executeHQL(params, hql, true, false, null, null);
	}
	
	protected List<BasePojo> executeHQLQuerys(Map params,String hql){
		return (List<BasePojo>) this.executeHQL(params, hql, true, true, null, null);
	}
	
	protected List<BasePojo> executeHQLQuerys(Map params,String hql, int start, int fetchSize){
		return (List<BasePojo>) this.executeHQL(params, hql, true, true, start, fetchSize);
	}
	
}